﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Diagnostics;
using System.Threading;

namespace Master
{
    /*
     * Map holds and keeps track of all elements that occur within the simulation.
     * 
     * Map is a Canvas that implements the singleton pattern and has the ability to search for specific elements that occur on a specific location.
     * The singleton pattern has been slightly altered to keep the Map compatible with XAML (which requires the default constructor)
     * 
     * @Version 0.3
     * */
    public class Map : Canvas
    {
        /*****************
         * STATIC FIELDS *
         *****************/
        public static Map INSTANCE;
        private static Boolean initialized = false;

        /**********************
         * STATIC CONSTRUCTOR *
         **********************/
        static Map() { }

        /**********
         * FIELDS *
         **********/
        private CarManager carManager;

        /***************
         * CONSTRUCTOR *
         ***************/
        public Map()
            : base()
        {
            if (Map.initialized)
                throw new InvalidOperationException();
            INSTANCE = this;
            Map.initialized = true;
            carManager = CarManager.getInstance();
        }

        /***********
         * METHODS *
         ***********/
        public void addCar(SpawnPoint spawnPoint)
        {
            carManager.addSingleCar(spawnPoint);
        }

        public void addCar(SpawnPoint spawnPoint, int numberOfCars)
        {
            Thread t = new Thread(delegate()
                {
                    carManager.addCar(spawnPoint, numberOfCars);
                });
            t.Start();
        }

        public void removeCar(Car car)
        {
            carManager.removeCar(car);
        }
        
        public Car getCarAtPoint(Point p)
        {
            return carManager.getCarAtPoint(p);
        }

        public UIElement getElementAt(Point p)
        {
            foreach (UIElement nextElement in Children)
            {
                if (!(nextElement is FrameworkElement)) 
                    continue;
                if (Toolkit.elementIsAtPoint(Toolkit.getPointRelativeToMap(nextElement), (FrameworkElement)nextElement, p))
                    return nextElement;
            }
            return null;
        }

        public Road getRoadAt(Point p)
        {
            foreach (UIElement nextElement in Children)
            {
                if (nextElement is Road && Toolkit.elementIsAtPoint(Toolkit.getPointRelativeToMap(nextElement), (FrameworkElement)nextElement, p))
                    return (Road)nextElement;
            }
            return null;
        }

        public Lane getLaneAt(Point p)
        {
            foreach (UIElement nextElement in Children)
            {
                if (!(nextElement is FrameworkElement)) continue;
                FrameworkElement frameWorkElement = (FrameworkElement)nextElement;
                if (!(Toolkit.elementIsAtPoint(Toolkit.getPointRelativeToMap(frameWorkElement), frameWorkElement, p))) 
                    continue;
                if (frameWorkElement is Lane) return (Lane) frameWorkElement;
                if (frameWorkElement is Road)
                {
                    Panel panel = (Panel)frameWorkElement;
                    foreach (UIElement child in panel.Children)
                    {
                        if (!(child is Lane)) continue;
                        if (Toolkit.elementIsAtPoint(Toolkit.getPointRelativeToMap(child), (Lane)child, p))
                            return (Lane)child;
                    }
                }
            }
            return null;
        }

        public InterSection getInterSectionAt(Point p)
        {
            foreach (UIElement nextElement in Children)
            {
                if (nextElement is InterSection && Toolkit.elementIsAtPoint(Toolkit.getPointRelativeToMap(nextElement), (FrameworkElement)nextElement, p))
                    return (InterSection)nextElement;
            }
            return null;
        }
    }
}
